<!DOCTYPE html>
<html>
<head>
    

    

    



    <meta charset="utf-8">
    
    
    
    <title>java内存管理 | 尼古拉斯-鱼丸博客 | 喝酒、吃肉、不赌</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="HotSpotjvm">
    <meta name="description" content="java垃圾回收在什么时候?程序员不能具体控制时间，系统在不可预测的时间调用System.gc()函数的时候；当然可以通过调优，用NewRatio控制newObject和oldObject的比例，用MaxTenuringThreshold 控制进入oldObject的次数，使得oldObject 存储空间延迟达到full gc,从而使得计时器引发gc时间延迟OOM的时间延迟，以延长对象生存期。 e">
<meta name="keywords" content="HotSpotjvm">
<meta property="og:type" content="article">
<meta property="og:title" content="java内存管理">
<meta property="og:url" content="http://fish_08.oschina.io/2018/01/02/java内存管理/index.html">
<meta property="og:site_name" content="尼古拉斯-鱼丸博客">
<meta property="og:description" content="java垃圾回收在什么时候?程序员不能具体控制时间，系统在不可预测的时间调用System.gc()函数的时候；当然可以通过调优，用NewRatio控制newObject和oldObject的比例，用MaxTenuringThreshold 控制进入oldObject的次数，使得oldObject 存储空间延迟达到full gc,从而使得计时器引发gc时间延迟OOM的时间延迟，以延长对象生存期。 e">
<meta property="og:image" content="http://fish_08.oschina.io/img/jvm/gss.jpg">
<meta property="og:image" content="http://fish_08.oschina.io/img/jvm/bjql.jpg">
<meta property="og:image" content="http://fish_08.oschina.io/img/jvm/fz.jpg">
<meta property="og:image" content="http://fish_08.oschina.io/img/jvm/bjzl.jpg">
<meta property="og:image" content="http://fish_08.oschina.io/img/jvm/neicun.jpg">
<meta property="og:updated_time" content="2018-01-02T08:52:27.564Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="java内存管理">
<meta name="twitter:description" content="java垃圾回收在什么时候?程序员不能具体控制时间，系统在不可预测的时间调用System.gc()函数的时候；当然可以通过调优，用NewRatio控制newObject和oldObject的比例，用MaxTenuringThreshold 控制进入oldObject的次数，使得oldObject 存储空间延迟达到full gc,从而使得计时器引发gc时间延迟OOM的时间延迟，以延长对象生存期。 e">
<meta name="twitter:image" content="http://fish_08.oschina.io/img/jvm/gss.jpg">
    
        <link rel="alternative" href="/atom.xml" title="尼古拉斯-鱼丸博客" type="application/atom+xml">
    
    <link rel="shortcut icon" href="/favicon.ico">
    <link rel="stylesheet" href="/css/style.css?v=1.6.7">
    <script>window.lazyScripts=[]</script>
</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap">
      <div class="brand">
        <a href="/" class="avatar waves-effect waves-circle waves-light">
          <img src="/img/avatar.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">于亮</h5>
          <a href="mailto:136214289@qq.com" title="136214289@qq.com" class="mail">136214289@qq.com</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                文档
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                标签
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="http://git.oschina.net/fish_08" target="_blank" >
                <i class="icon icon-lg icon-github"></i>
                Github
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="http://weibo.com/2837335591/profile?rightmod=1&wvr=6&mod=personinfo" target="_blank" >
                <i class="icon icon-lg icon-weibo"></i>
                Weibo
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/custom"  >
                <i class="icon icon-lg icon-link"></i>
                测试
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">java内存管理</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">java内存管理</h1>
        <h5 class="subtitle">
            
                <time datetime="2018-01-02T05:58:58.000Z" itemprop="datePublished" class="page-time">
  2018-01-02
</time>


            
        </h5>
    </div>

    

</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#java垃圾回收在什么时候"><span class="post-toc-number">1.</span> <span class="post-toc-text">java垃圾回收在什么时候?</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#java垃圾回收什么东西"><span class="post-toc-number">2.</span> <span class="post-toc-text">java垃圾回收什么东西?</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#java垃圾回收做什么"><span class="post-toc-number">3.</span> <span class="post-toc-text">java垃圾回收做什么?</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#JVM-GC（垃圾回收机制）"><span class="post-toc-number">4.</span> <span class="post-toc-text">JVM GC（垃圾回收机制）</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#JVM-GC回收哪个区域内的垃圾？"><span class="post-toc-number">5.</span> <span class="post-toc-text">JVM GC回收哪个区域内的垃圾？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#JVM-GC怎么判断对象可以被回收了？"><span class="post-toc-number">6.</span> <span class="post-toc-text">JVM GC怎么判断对象可以被回收了？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#JVM-GC什么时候执行？"><span class="post-toc-number">7.</span> <span class="post-toc-text">JVM GC什么时候执行？</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#按代的垃圾回收机制"><span class="post-toc-number">8.</span> <span class="post-toc-text">按代的垃圾回收机制</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#JVM-GC-算法讲解"><span class="post-toc-number">9.</span> <span class="post-toc-text">JVM GC 算法讲解</span></a></li><li class="post-toc-item post-toc-level-3"><a class="post-toc-link" href="#垃圾回收器简介"><span class="post-toc-number">10.</span> <span class="post-toc-text">垃圾回收器简介</span></a></li></ol>
        </nav>
    </aside>
    
<article id="post-java内存管理"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">java内存管理</h1>
        <div class="post-meta">
            <time class="post-time" title="2018年01月02日 13:58" datetime="2018-01-02T05:58:58.000Z"  itemprop="datePublished">2018-01-02</time>

            


            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


            

        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <h3 id="java垃圾回收在什么时候"><a href="#java垃圾回收在什么时候" class="headerlink" title="java垃圾回收在什么时候?"></a>java垃圾回收在什么时候?</h3><p>程序员不能具体控制时间，系统在不可预测的时间调用System.gc()函数的时候；当然可以通过调优，用NewRatio控制newObject和oldObject的比例，用MaxTenuringThreshold 控制进入oldObject的次数，使得oldObject 存储空间延迟达到full gc,从而使得计时器引发gc时间延迟OOM的时间延迟，以延长对象生存期。<br> eden满了minor gc<br> 升到老年代的对象大于老年代剩余空间full gc，或者小于时被HandlePromotionFailure参数强制full gc<br> gc与非gc时间耗时超过了GCTimeRatio的限制引发OOM，<br> 调优诸如通过NewRatio控制新生代老年代比例，通过MaxTenuringThreshold控制进入老年前生存次数等 </p>
<p> <strong>虚拟机参数</strong></p>
<p>-Xmx3550m：设置JVM最大可用内存为3550M。<br>-Xms3550m：设置JVM促使内存为3550m。此值可以设置与-Xmx相同，以避免每次垃圾回收完成后JVM重新分配内存。<br>-Xmn2g：设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m，所以增大年轻代后，将会减小年老代大小。此值对系统性能影响较大，Sun官方推荐配置为整个堆的3/8。<br>-Xss128k：设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M，以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下，减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的，不能无限生成，经验值在3000~5000左右。<br>-XX:NewRatio=4:设置年轻代（包括Eden和两个Survivor区）与年老代的比值（除去持久代）。设置为4，则年轻代与年老代所占比值为1：4，年轻代占整个堆栈的1/5<br>-XX:SurvivorRatio=4：设置年轻代中Eden区与Survivor区的大小比值。设置为4，则两个Survivor区与一个Eden区的比值为2:4，一个Survivor区占整个年轻代的1/6<br>-XX:MaxPermSize=16m:设置持久代大小为16m。<br>-XX:MaxTenuringThreshold=0：设置垃圾最大年龄。如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代。对于年老代比较多的应用，可以提高效率。如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间，增加在年轻代即被回收的概论。<br> new Ratio:Eden+2Survivor/年老代<br> MaxTenuringThreshold: 设置对象进入老年代的年龄阀值,即对象在新生代中最大的存活次数，最大值15，默认15.如果设置为0的话，则年轻代对象不经过Survivor区，直接进入年老代。对于年老代比较多的应用，可以提高效率。如果将此值设置为一个较大值，则年轻代对象会在Survivor区进行多次复制，这样可以增加对象再年轻代的存活时间，增加在年轻代即被回收的概论。<br> 虚拟机给每个对象定义了一个对象年龄（Age）计数器。如果对象在 Eden 出生并经过第一次 Minor GC 后仍然存活，并且能被 Survivor 容纳的话，将被移动到 Survivor 空间中，并将对象年龄设为 1。对象在 Survivor 区中每熬过一次 Minor GC，年龄就增加 1 岁，当它的年龄增加到一定程度（默认为 15 岁）时，就会被晋升到老年代中。对象晋升老年代的年龄阈值，可以通过参数 -XX:MaxTenuringThreshold (阈值)来设置<br> HandlePromotionFailure 参数已经废弃，默认判断。<br><strong>调优经验</strong><br>年轻代大小选择<br>   响应时间优先的应用：尽可能设大，直到接近系统的最低响应时间限制（根据实际情况选择）。在此种情况下，年轻代收集发生的频率也是最小的。同时，减少到达年老代的对象。<br>吞吐量优先的应用：尽可能的设置大，可能到达Gbit的程度。因为对响应时间没有要求，垃圾收集可以并行进行，一般适合8CPU以上的应用。<br>年老代大小选择<br>   响应时间优先的应用：年老代使用并发收集器，所以其大小需要小心设置，一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了，可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式；如果堆大了，则需要较长的收集时间。最优化的方案，一般需要参考以下数据获得：<br>   并发垃圾收集信息<br>   持久代并发收集次数<br>   传统GC信息<br>   花在年轻代和年老代回收上的时间比例<br>   减少年轻代和年老代花费的时间，一般会提高应用的效率<br>   吞吐量优先的应用：一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是，这样可以尽可能回收掉大部分短期对象，减少中期的对象，而年老代尽存放长期存活对象。<br>   Java整个堆大小设置，Xmx 和 Xms设置为老年代存活对象的3-4倍，即FullGC之后的老年代内存占用的3-4倍</p>
<p><strong>调优方式</strong><br>大小推荐：永久代 PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍。<br>          年轻代Xmn的设置为老年代存活对象的1-1.5倍。<br>          老年代的内存大小设置为老年代存活对象的2-3倍。</p>
<p>如何确认老年代存活对象大小？<br>方式1（推荐/比较稳妥）：<br>  JVM参数中添加GC日志，GC日志中会记录每次FullGC之后各代的内存大小，观察老年代GC之后的空间大小。可观察一段时间内（比如2天）的FullGC之后的内存情况，根据多次的FullGC之后的老年代的空间大小数据来预估FullGC之后老年代的存活对象大小（可根据多次FullGC之后的内存大小取平均值）<br>   如何触发FullGC ？<br>使用jmap工具可触发FullGC<br>jmap -dump:live,format=b,file=heap.bin将当前的存活对象dump到文件，此时会触发FullGC<br>jmap -histo:live打印每个class的实例数目,内存占用,类全名信息.live子参数加上后,只统计活的对象数量. 此时会触发FullGC</p>
<p><strong>名词解释</strong><br>Minor GC触发机制：<br>当年轻代满时就会触发Minor GC，这里的年轻代满指的是Eden代满，Survivor满不会引发GC<br>Full GC触发机制：<br>当年老代满时会引发Full GC，Full GC将会同时回收年轻代、年老代，<br>当永久代满时也会引发Full GC，会导致Class、Method元信息的卸载<br>调优主要是减少 Full GC 的触发次数，可以通过 NewRatio 控制新生代转老年代的比例，通过MaxTenuringThreshold 设置对象进入老年代的年龄阀值；</p>
<p>空间分配担保：<br>在发生Minor GC之前，虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间，如果这个条件成立，那么Minor GC可以确保是安全的。如果不成立，则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许，那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小，如果大于，将尝试着进行一次Minor GC，尽管这次Minor GC是有风险的；如果小于，或者HandlePromotionFailure设置不允许冒险，那这时也要改为进行一次Full GC。<br>下面解释一下“冒险”是冒了什么风险，前面提到过，新生代使用复制收集算法，但为了内存利用率，只使用其中一个Survivor空间来作为轮换备份，因此当出现大量对象在Minor GC后仍然存活的情况（最极端的情况就是内存回收后新生代中所有对象都存活），就需要老年代进行分配担保，把Survivor无法容纳的对象直接进入老年代。与生活中的贷款担保类似，老年代要进行这样的担保，前提是老年代本身还有容纳这些对象的剩余空间，一共有多少对象会活下来在实际完成内存回收之前是无法明确知道的，所以只好取之前每一次回收晋升到老年代对象容量的平均大小值作为经验值，与老年代的剩余空间进行比较，决定是否进行Full GC来让老年代腾出更多空间。<br>取平均值进行比较其实仍然是一种动态概率的手段，也就是说，如果某次Minor GC存活后的对象突增，远远高于平均值的话，依然会导致担保失败（Handle Promotion Failure）。如果出现了HandlePromotionFailure失败，那就只好在失败后重新发起一次Full GC。虽然担保失败时绕的圈子是最大的，但大部分情况下都还是会将HandlePromotionFailure开关打开，避免Full GC过于频繁</p>
<h3 id="java垃圾回收什么东西"><a href="#java垃圾回收什么东西" class="headerlink" title="java垃圾回收什么东西?"></a>java垃圾回收什么东西?</h3><p>超出了作用域或引用计数为空的对象；从gc root开始搜索找不到的对象，而且经过一次标记、清理，仍然没有复活的对象</p>
<h3 id="java垃圾回收做什么"><a href="#java垃圾回收做什么" class="headerlink" title="java垃圾回收做什么?"></a>java垃圾回收做什么?</h3><p>删除不使用的对象，回收内存空间；运行默认的finalize,当然程序员想立刻调用就用dipose调用以释放资源如文件句柄，JVM用from survivor、to survivor对它进行标记清理，对象序列化后也可以使它复活。<br>新生代做的是复制清理、from survivor、to survivor是干啥用的、老年代做的是标记清理、标记清理后碎片要不要整理、复制清理和标记清理;<br>讲清楚串行、并行（整理/不整理碎片）、CMS等搜集器可作用的年代、特点、优劣势，并且能说明控制/调整收集器选择的方式;</p>
<h3 id="JVM-GC（垃圾回收机制）"><a href="#JVM-GC（垃圾回收机制）" class="headerlink" title="JVM GC（垃圾回收机制）"></a>JVM GC（垃圾回收机制）</h3><p>stop-the-world 。它会在任何一种GC算法中发生。stop-the-world 意味着JVM因为需要执行GC而停止了应用程序的执行。当stop-the-world 发生时，除GC所需的线程外，所有的线程都进入等待状态，直到GC任务完成。GC优化很多时候就是减少stop-the-world 的发生。</p>
<h3 id="JVM-GC回收哪个区域内的垃圾？"><a href="#JVM-GC回收哪个区域内的垃圾？" class="headerlink" title="JVM GC回收哪个区域内的垃圾？"></a>JVM GC回收哪个区域内的垃圾？</h3><p>JVM GC只回收堆区和方法区内的对象。而栈区的数据，在超出作用域后会被JVM自动释放掉，所以其不在JVM GC的管理范围内。</p>
<h3 id="JVM-GC怎么判断对象可以被回收了？"><a href="#JVM-GC怎么判断对象可以被回收了？" class="headerlink" title="JVM GC怎么判断对象可以被回收了？"></a>JVM GC怎么判断对象可以被回收了？</h3><p>对象没有引用<br>· 作用域发生未捕获异常<br>· 程序在作用域正常执行完毕<br>· 程序执行了System.exit()<br>· 程序发生意外终止（被杀线程等）<br>在Java程序中不能显式的分配和注销缓存，因为这些事情JVM都帮我们做了，那就是GC。<br>有些时候我们可以将相关的对象设置成null 来试图显示的清除缓存，但是并不是设置为null 就会一定被标记为可回收，有可能会发生逃逸。<br>将对象设置成null 至少没有什么坏处，但是使用System.gc() 便不可取了，使用System.gc() 时候并不是马上执行GC操作，而是会等待一段时间，甚至不执行，而且System.gc() 如果被执行，会触发Full GC ，这非常影响性能。</p>
<h3 id="JVM-GC什么时候执行？"><a href="#JVM-GC什么时候执行？" class="headerlink" title="JVM GC什么时候执行？"></a>JVM GC什么时候执行？</h3><p>eden区空间不够存放新对象的时候，执行Minro GC。升到老年代的对象大于老年代剩余空间的时候执行Full GC，或者小于的时候被HandlePromotionFailure 参数强制Full GC 。调优主要是减少 Full GC 的触发次数，可以通过 NewRatio 控制新生代转老年代的比例，通过MaxTenuringThreshold 设置对象进入老年代的年龄阀值（后面会介绍到）。</p>
<h3 id="按代的垃圾回收机制"><a href="#按代的垃圾回收机制" class="headerlink" title="按代的垃圾回收机制"></a>按代的垃圾回收机制</h3><p>新生代（Young generation）：绝大多数最新被创建的对象都会被分配到这里，由于大部分在创建后很快变得不可达，很多对象被创建在新生代，然后“消失”。对象从这个区域“消失”的过程我们称之为：Minor GC 。<br>老年代（Old generation）：对象没有变得不可达，并且从新生代周期中存活了下来，会被拷贝到这里。其区域分配的空间要比新生代多。也正由于其相对大的空间，发生在老年代的GC次数要比新生代少得多。对象从老年代中消失的过程，称之为：Major GC 或者 Full GC。<br>持久代（Permanent generation）也称之为 方法区（Method area）：用于保存类常量以及字符串常量。注意，这个区域不是用于存储那些从老年代存活下来的对象，这个区域也可能发生GC。发生在这个区域的GC事件也被算为 Major GC 。只不过在这个区域发生GC的条件非常严苛，必须符合以下三种条件才会被回收：<br>1、所有实例被回收<br>2、加载该类的ClassLoader 被回收<br>3、Class 对象无法通过任何途径访问（包括反射）</p>
<p>可能我们会有疑问：<br>如果老年代的对象需要引用新生代的对象，会发生什么呢？<br>为了解决这个问题，老年代中存在一个 card table ，它是一个512byte大小的块。所有老年代的对象指向新生代对象的引用都会被记录在这个表中。当针对新生代执行GC的时候，只需要查询 card table 来决定是否可以被回收，而不用查询整个老年代。这个 card table 由一个write barrier 来管理。write barrier给GC带来了很大的性能提升，虽然由此可能带来一些开销，但完全是值得的。<br>默认的新生代（Young generation）、老年代（Old generation）所占空间比例为 1 : 2 。</p>
<p>新生代空间的构成与逻辑<br>为了更好的理解GC，我们来学习新生代的构成，它用来保存那些第一次被创建的对象，它被分成三个空间：<br>· 一个伊甸园空间（Eden）<br>· 两个幸存者空间（Fron Survivor、To Survivor）<br>默认新生代空间的分配：Eden : Fron : To = 8 : 1 : 1<br>每个空间的执行顺序如下：<br>1、绝大多数刚刚被创建的对象会存放在伊甸园空间（Eden）。<br>2、在伊甸园空间执行第一次GC（Minor GC）之后，存活的对象被移动到其中一个幸存者空间（Survivor）。<br>3、此后，每次伊甸园空间执行GC后，存活的对象会被堆积在同一个幸存者空间。<br>4、当一个幸存者空间饱和，还在存活的对象会被移动到另一个幸存者空间。然后会清空已经饱和的哪个幸存者空间。<br>5、在以上步骤中重复N次（N = MaxTenuringThreshold（年龄阀值设定，默认15））依然存活的对象，就会被移动到老年代。<br>从上面的步骤可以发现，两个幸存者空间，必须有一个是保持空的。如果两个两个幸存者空间都有数据，或两个空间都是空的，那一定是你的系统出现了某种错误。<br>我们需要重点记住的是，对象在刚刚被创建之后，是保存在伊甸园空间的（Eden）。那些长期存活的对象会经由幸存者空间（Survivor）转存到老年代空间（Old generation）。<br>也有例外出现，对于一些比较大的对象（需要分配一块比较大的连续内存空间）则直接进入到老年代。一般在Survivor 空间不足的情况下发生。</p>
<p>老年代空间的构成与逻辑<br>老年代空间的构成其实很简单，它不像新生代空间那样划分为几个区域，它只有一个区域，里面存储的对象并不像新生代空间绝大部分都是朝闻道，夕死矣。这里的对象几乎都是从Survivor 空间中熬过来的，它们绝不会轻易的狗带。因此，Full GC（Major GC）发生的次数不会有Minor GC 那么频繁，并且做一次Major GC 的时间比Minor GC 要更长（约10倍）。</p>
<h3 id="JVM-GC-算法讲解"><a href="#JVM-GC-算法讲解" class="headerlink" title="JVM GC 算法讲解"></a>JVM GC 算法讲解</h3><p>1、根搜索算法<br>根搜索算法是从离散数学中的图论引入的，程序把所有引用关系看作一张图，从一个节点GC ROOT 开始，寻找对应的引用节点，找到这个节点后，继续寻找这个节点的引用节点。当所有的引用节点寻找完毕后，剩余的节点则被认为是没有被引用到的节点，即无用的节点。<br>红色为无用的节点，可以被回收。</p>
<p>目前Java中可以作为GC ROOT的对象有：<br><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="/img/jvm/gss.jpg" alt="根搜索" title="">
                </div>
                <div class="image-caption">根搜索</div>
            </figure><br>1、虚拟机栈中引用的对象（本地变量表）<br>2、方法区中静态属性引用的对象<br>3、方法区中常亮引用的对象<br>4、本地方法栈中引用的对象（Native对象）<br>基本所有GC算法都引用根搜索算法这种概念。</p>
<p>2、标记 - 清除算法<br>标记-清除算法采用从根集合进行扫描，对存活的对象进行标记，标记完毕后，再扫描整个空间中未被标记的对象进行直接回收，如上图。<br>标记-清除算法不需要进行对象的移动，并且仅对不存活的对象进行处理，在存活的对象比较多的情况下极为高效，但由于标记-清除算法直接回收不存活的对象，并没有对还存活的对象进行整理，因此会导致内存碎片。<br><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="/img/jvm/bjql.jpg" alt="标记" title="">
                </div>
                <div class="image-caption">标记</div>
            </figure></p>
<p>3、复制算法<br>复制算法将内存划分为两个区间，使用此算法时，所有动态分配的对象都只能分配在其中一个区间（活动区间），而另外一个区间（空间区间）则是空闲的。<br>复制算法采用从根集合扫描，将存活的对象复制到空闲区间，当扫描完毕活动区间后，会的将活动区间一次性全部回收。此时原本的空闲区间变成了活动区间。下次GC时候又会重复刚才的操作，以此循环。<br><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="/img/jvm/fz.jpg" alt="复制算法" title="">
                </div>
                <div class="image-caption">复制算法</div>
            </figure><br>复制算法在存活对象比较少的时候，极为高效，但是带来的成本是牺牲一半的内存空间用于进行对象的移动。所以复制算法的使用场景，必须是对象的存活率非常低才行，而且最重要的是，我们需要克服50%内存的浪费。</p>
<p>4、标记 - 整理算法<br>标记-整理算法采用 标记-清除 算法一样的方式进行对象的标记、清除，但在回收不存活的对象占用的空间后，会将所有存活的对象往左端空闲空间移动，并更新对应的指针。标记-整理 算法是在标记-清除 算法之上，又进行了对象的移动排序整理，因此成本更高，但却解决了内存碎片的问题。<br>JVM为了优化内存的回收，使用了分代回收的方式，对于新生代内存的回收（Minor GC）主要采用复制算法。而对于老年代的回收（Major GC），大多采用标记-整理算法。<br><figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="/img/jvm/bjzl.jpg" alt="标记" title="">
                </div>
                <div class="image-caption">标记</div>
            </figure></p>
<h3 id="垃圾回收器简介"><a href="#垃圾回收器简介" class="headerlink" title="垃圾回收器简介"></a>垃圾回收器简介</h3><p>需要注意的是，每一个回收器都存在Stop The World 的问题，只不过各个回收器在Stop The World 时间优化程度、算法的不同，可根据自身需求选择适合的回收器。<br>1、Serial（-XX:+UseSerialGC）<br>从名字我们可以看出，这是一个串行收集器。<br>Serial收集器是Java虚拟机中最基本、历史最悠久的收集器。在JDK1.3之前是Java虚拟机新生代收集器的唯一选择。目前也是ClientVM下ServerVM 4核4GB以下机器默认垃圾回收器。Serial收集器并不是只能使用一个CPU进行收集，而是当JVM需要进行垃圾回收的时候，需暂停所有的用户线程，直到回收结束。<br>使用算法：复制算法</p>
<p>JVM中文名称为Java虚拟机，因此它像一台虚拟的电脑在工作，而其中的每一个线程都被认为是JVM的一个处理器，因此图中的CPU0、CPU1实际上为用户的线程，而不是真正的机器CPU，不要误解哦。<br>Serial收集器虽然是最老的，但是它对于限定单个CPU的环境来说，由于没有线程交互的开销，专心做垃圾收集，所以它在这种情况下是相对于其他收集器中最高效的。</p>
<p>2、SerialOld（-XX:+UseSerialGC）<br>SerialOld是Serial收集器的老年代收集器版本，它同样是一个单线程收集器，这个收集器目前主要用于Client模式下使用。如果在Server模式下，它主要还有两大用途：一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用，另外一个就是作为CMS收集器的后备预案，如果CMS出现Concurrent Mode Failure，则SerialOld将作为后备收集器。<br>使用算法：标记 - 整理算法<br>运行示意图与上图一致。</p>
<p>3、ParNew（-XX:+UseParNewGC）<br>ParNew其实就是Serial收集器的多线程版本。除了Serial收集器外，只有它能与CMS收集器配合工作。<br>使用算法：复制算法<br>ParNew是许多运行在Server模式下的JVM首选的新生代收集器。但是在单CPU的情况下，它的效率远远低于Serial收集器，所以一定要注意使用场景。</p>
<p>4、ParallelScavenge（-XX:+UseParallelGC）<br>ParallelScavenge又被称为吞吐量优先收集器，和ParNew 收集器类似，是一个新生代收集器。<br>使用算法：复制算法<br>ParallelScavenge收集器的目标是达到一个可控件的吞吐量，所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值，即吞吐量 = 运行用户代码时间 / （运行用户代码时间 + 垃圾收集时间）。如果虚拟机总共运行了100分钟，其中垃圾收集花了1分钟，那么吞吐量就是99% 。</p>
<p>5、ParallelOld（-XX:+UseParallelOldGC）<br>ParallelOld是并行收集器，和SerialOld一样，ParallelOld是一个老年代收集器，是老年代吞吐量优先的一个收集器。这个收集器在JDK1.6之后才开始提供的，在此之前，ParallelScavenge只能选择SerialOld来作为其老年代的收集器，这严重拖累了ParallelScavenge整体的速度。而ParallelOld的出现后，“吞吐量优先”收集器才名副其实！<br>使用算法：标记 - 整理算法</p>
<p>在注重吞吐量与CPU数量大于1的情况下，都可以优先考虑ParallelScavenge + ParalleloOld收集器。</p>
<p>6、CMS （-XX:+UseConcMarkSweepGC）<br>CMS是一个老年代收集器，全称 Concurrent Low Pause Collector，是JDK1.4后期开始引用的新GC收集器，在JDK1.5、1.6中得到了进一步的改进。它是对于响应时间的重要性需求大于吞吐量要求的收集器。对于要求服务器响应速度高的情况下，使用CMS非常合适。<br>CMS的一大特点，就是用两次短暂的暂停来代替串行或并行标记整理算法时候的长暂停。<br>使用算法：标记 - 清理</p>
<p>CMS的执行过程如下：<br>· 初始标记（STW initial mark）<br>在这个阶段，需要虚拟机停顿正在执行的应用线程，官方的叫法STW（Stop Tow World）。这个过程从根对象扫描直接关联的对象，并作标记。这个过程会很快的完成。<br>· 并发标记（Concurrent marking）<br>这个阶段紧随初始标记阶段，在“初始标记”的基础上继续向下追溯标记。注意这里是并发标记，表示用户线程可以和GC线程一起并发执行，这个阶段不会暂停用户的线程哦。<br>· 并发预清理（Concurrent precleaning）<br>这个阶段任然是并发的，JVM查找正在执行“并发标记”阶段时候进入老年代的对象（可能这时会有对象从新生代晋升到老年代，或被分配到老年代）。通过重新扫描，减少在一个阶段“重新标记”的工作，因为下一阶段会STW。<br>· 重新标记（STW remark）<br>这个阶段会再次暂停正在执行的应用线程，重新重根对象开始查找并标记并发阶段遗漏的对象（在并发标记阶段结束后对象状态的更新导致），并处理对象关联。这一次耗时会比“初始标记”更长，并且这个阶段可以并行标记。<br>· 并发清理（Concurrent sweeping）<br>这个阶段是并发的，应用线程和GC清除线程可以一起并发执行。<br>· 并发重置（Concurrent reset）<br>这个阶段任然是并发的，重置CMS收集器的数据结构，等待下一次垃圾回收。</p>
<p>CMS的缺点：<br>1、内存碎片。由于使用了 标记-清理 算法，导致内存空间中会产生内存碎片。不过CMS收集器做了一些小的优化，就是把未分配的空间汇总成一个列表，当有JVM需要分配内存空间的时候，会搜索这个列表找到符合条件的空间来存储这个对象。但是内存碎片的问题依然存在，如果一个对象需要3块连续的空间来存储，因为内存碎片的原因，寻找不到这样的空间，就会导致Full GC。<br>2、需要更多的CPU资源。由于使用了并发处理，很多情况下都是GC线程和应用线程并发执行的，这样就需要占用更多的CPU资源，也是牺牲了一定吞吐量的原因。<br>3、需要更大的堆空间。因为CMS标记阶段应用程序的线程还是执行的，那么就会有堆空间继续分配的问题，为了保障CMS在回收堆空间之前还有空间分配给新加入的对象，必须预留一部分空间。CMS默认在老年代空间使用68%时候启动垃圾回收。可以通过-XX:CMSinitiatingOccupancyFraction=n来设置这个阀值。</p>
<p>7、GarbageFirst（G1）</p>
<p>通过重新划分内存区域，整合优化CMS，同时注重吞吐量和响应时间。</p>
<p>整理一下新生代和老年代的收集器。<br>新生代收集器：<br>Serial （-XX:+UseSerialGC）<br>ParNew（-XX:+UseParNewGC）<br>ParallelScavenge（-XX:+UseParallelGC）<br>G1 收集器</p>
<p>老年代收集器：<br>SerialOld（-XX:+UseSerialOldGC）<br>ParallelOld（-XX:+UseParallelOldGC）<br>CMS（-XX:+UseConcMarkSweepGC）<br>G1 收集器</p>
<figure class="image-bubble">
                <div class="img-lightbox">
                    <div class="overlay"></div>
                    <img src="/img/jvm/neicun.jpg" alt="根搜索" title="">
                </div>
                <div class="image-caption">根搜索</div>
            </figure>
        </div>

        <blockquote class="post-copyright">
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2018-01-02T08:52:27.564Z" itemprop="dateUpdated">2018年1月2日 16:52</time>
</span><br>


        如要转载请注明出处：<a href="/2018/01/02/java内存管理/" target="_blank" rel="external">http://fish_08.oschina.io/2018/01/02/java内存管理/</a>
    </div>
    <footer>
        <a href="http://fish_08.oschina.io">
            <img src="/img/avatar.jpg" alt="于亮">
            于亮
        </a>
    </footer>
</blockquote>

        
<div class="page-reward">
    <a id="rewardBtn" href="javascript:;" class="page-reward-btn waves-effect waves-circle waves-light">赏</a>
</div>



        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/HotSpotjvm/">HotSpotjvm</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://fish_08.oschina.io/2018/01/02/java内存管理/&title=《java内存管理》 — 尼古拉斯-鱼丸博客&pic=http://fish_08.oschina.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://fish_08.oschina.io/2018/01/02/java内存管理/&title=《java内存管理》 — 尼古拉斯-鱼丸博客&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://fish_08.oschina.io/2018/01/02/java内存管理/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《java内存管理》 — 尼古拉斯-鱼丸博客&url=http://fish_08.oschina.io/2018/01/02/java内存管理/&via=http://fish_08.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://fish_08.oschina.io/2018/01/02/java内存管理/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between flex-row-reverse">
  

  
    <div class="waves-block waves-effect next">
      <a href="/2017/11/30/大型网站技术架构5-网站安全性/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">大型网站技术架构5-网站安全性</h4>
      </a>
    </div>
  
</nav>



    







</article>

<div id="reward" class="page-modal reward-lay">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <h3 class="reward-title">
        <i class="icon icon-quote-left"></i>
        谢谢包子~
        <i class="icon icon-quote-right"></i>
    </h3>
    <ul class="reward-items">
        
        <li>
            <img src="/img/wechat.png" title="微信打赏二维码" alt="微信打赏二维码">
            <p>微信</p>
        </li>
        

        
        <li>
            <img src="/img/alipay.png" title="支付宝打赏二维码" alt="支付宝打赏二维码">
            <p>支付宝</p>
        </li>
        
    </ul>
</div>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            <span><a href="/atom.xml" target="_blank" class="rss" title="rss"><i class="icon icon-lg icon-rss"></i></a></span>
            <span>博客内容遵循 <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p>
            <span>Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="http://fish_08.oschina.io/" target="_blank">yuliang</a></span>
            <span>尼古拉斯-鱼丸博客 &copy; 2015 - 2018</span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://fish_08.oschina.io/2018/01/02/java内存管理/&title=《java内存管理》 — 尼古拉斯-鱼丸博客&pic=http://fish_08.oschina.io/img/avatar.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://fish_08.oschina.io/2018/01/02/java内存管理/&title=《java内存管理》 — 尼古拉斯-鱼丸博客&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://fish_08.oschina.io/2018/01/02/java内存管理/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《java内存管理》 — 尼古拉斯-鱼丸博客&url=http://fish_08.oschina.io/2018/01/02/java内存管理/&via=http://fish_08.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://fish_08.oschina.io/2018/01/02/java内存管理/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/', SHARE: true, REWARD: true };



</script>

<script src="/js/main.min.js?v=1.6.7"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="/js/search.min.js?v=1.6.7" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>


</body>
</html>
